import time
import random
import csv
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.edge.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
from selenium.common.exceptions import TimeoutException, ElementClickInterceptedException, NoSuchElementException, WebDriverException

def random_sleep(min_seconds=2, max_seconds=5):
    """
    随机延迟函数，模拟人类行为
    """
    time.sleep(random.uniform(min_seconds, max_seconds))

def safe_click(driver, element, max_retries=3):
    """
    安全点击元素，处理可能的点击拦截
    """
    for i in range(max_retries):
        try:
            # 确保元素在视图中
            driver.execute_script("arguments[0].scrollIntoView({behavior: 'smooth', block: 'center'});", element)
            random_sleep(0.5, 1)
            
            # 使用 ActionChains 进行点击
            action = ActionChains(driver)
            action.move_to_element(element)
            action.pause(random.uniform(0.5, 1))
            action.click()
            action.perform()
            return True
        except ElementClickInterceptedException:
            if i == max_retries - 1:
                raise
            random_sleep(1, 2)
    return False

def login_jd(driver, max_retries=3):
    """
    登录京东网站
    """
    for attempt in range(max_retries):
        try:
            print(f"尝试登录 (第 {attempt + 1} 次)")
            
            # 访问京东首页
            driver.get("https://www.jd.com/")
            random_sleep(5, 8)

            # 尝试多种方式定位登录按钮
            login_button = None
            selectors = [
                (By.CSS_SELECTOR, "a.link-login"),
                (By.CSS_SELECTOR, "a[href*='login.jd.com']"),
                (By.CSS_SELECTOR, "a[href*='passport.jd.com']"),
                (By.XPATH, "//a[contains(text(), '登录')]"),
                (By.XPATH, "//a[contains(@class, 'login')]")
            ]

            # 遍历所有可能的选择器
            for by, selector in selectors:
                try:
                    login_button = WebDriverWait(driver, 10).until(
                        EC.presence_of_element_located((by, selector))
                    )
                    if login_button and login_button.is_displayed():
                        break
                except:
                    continue

            if not login_button:
                raise Exception("无法找到登录按钮")

            # 安全点击登录按钮
            safe_click(driver, login_button)
            random_sleep(3, 5)

            # 切换到账号密码登录
            pwd_login = WebDriverWait(driver, 15).until(
                EC.presence_of_element_located((By.CSS_SELECTOR, "div.login-tab-r, #pwd-login"))
            )
            safe_click(driver, pwd_login)
            random_sleep(3, 5)

            # 定位并输入账号密码
            username_input = WebDriverWait(driver, 15).until(
                EC.presence_of_element_located((By.ID, "loginname"))
            )
            password_input = WebDriverWait(driver, 15).until(
                EC.presence_of_element_located((By.ID, "nloginpwd"))
            )

            # 清空输入框
            username_input.clear()
            password_input.clear()
            random_sleep(1, 2)

            # 模拟人类输入账号
            for char in "13208546857":
                username_input.send_keys(char)
                random_sleep(0.1, 0.3)
            
            # 模拟人类输入密码
            for char in "JYX20031015":
                password_input.send_keys(char)
                random_sleep(0.1, 0.3)

            random_sleep(2, 4)

            # 点击登录按钮
            submit_button = WebDriverWait(driver, 15).until(
                EC.element_to_be_clickable((By.ID, "loginsubmit"))
            )
            safe_click(driver, submit_button)
            random_sleep(5, 8)

            # 等待登录成功标志
            WebDriverWait(driver, 20).until(
                EC.presence_of_element_located((By.CLASS_NAME, "nickname"))
            )
            
            print("登录成功！")
            return True

        except Exception as e:
            print(f"登录失败 (第 {attempt + 1} 次): {str(e)}")
            if attempt == max_retries - 1:
                raise
            random_sleep(5, 10)
    
    return False

def get_product_comments(driver, product_url):
    """
    获取商品评论数据
    """
    try:
        # 等待页面加载完成
        WebDriverWait(driver, 20).until(
            lambda d: d.execute_script("return document.readyState") == "complete"
        )
        
        # 从URL中提取商品ID
        sku_id = product_url.split('/')[-1].split('.')[0]
        
        # 获取商品名称
        product_name = WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, "div.sku-name"))
        ).text.strip()
        
        # 滚动到评论区域
        comment_tab = WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, "li[data-anchor='#comment']"))
        )
        driver.execute_script("arguments[0].scrollIntoView({behavior: 'smooth', block: 'center'});", comment_tab)
        random_sleep(1, 2)
        
        # 点击评论标签
        safe_click(driver, comment_tab)
        random_sleep(2, 3)
        
        # 等待评论加载
        WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, "div.comment-item"))
        )
        
        # 获取评论数据
        comments = []
        comment_elements = driver.find_elements(By.CSS_SELECTOR, "div.comment-item")
        
        # 解析每条评论
        for comment in comment_elements:
            try:
                comment_data = {
                    'sku_id': sku_id,
                    'item_name': product_name,
                    'content': comment.find_element(By.CSS_SELECTOR, "div.comment-content").text.strip(),
                    'creation_time': comment.find_element(By.CSS_SELECTOR, "div.comment-time").text.strip(),
                    'score': len(comment.find_elements(By.CSS_SELECTOR, "span.star-yellow")),
                    'user_level_name': comment.find_element(By.CSS_SELECTOR, "div.user-info span").text.strip()
                }
                
                # 尝试获取用户昵称
                try:
                    comment_data['nickname'] = comment.find_element(By.CSS_SELECTOR, "div.user-info a").text.strip()
                except:
                    comment_data['nickname'] = ''
                    
                comments.append(comment_data)
            except NoSuchElementException:
                continue
                
        return comments
    except Exception as e:
        print(f"获取评论时出错: {str(e)}")
        return None

def scrape_products(driver, max_pages=5):
    """
    爬取商品列表和评论
    """
    # 创建CSV文件并写入表头
    with open('京东评论数据.csv', 'w', encoding='utf-8', newline='') as f:
        writer = csv.DictWriter(f, fieldnames=[
            'sku_id', 'item_name', 'content', 'creation_time', 
            'score', 'user_level_name', 'nickname'
        ])
        writer.writeheader()
        
        current_page = 1
        while current_page <= max_pages:
            try:
                print(f"正在爬取第 {current_page} 页...")
                
                # 等待页面加载完成
                WebDriverWait(driver, 20).until(
                    lambda d: d.execute_script("return document.readyState") == "complete"
                )
                
                # 等待商品列表加载
                try:
                    WebDriverWait(driver, 20).until(
                        EC.presence_of_element_located((By.CSS_SELECTOR, "div.gl-item"))
                    )
                except TimeoutException:
                    print("等待商品列表超时，尝试刷新页面...")
                    driver.refresh()
                    random_sleep(5, 8)
                    continue
                
                # 滚动页面以加载所有商品
                last_height = driver.execute_script("return document.body.scrollHeight")
                while True:
                    driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
                    random_sleep(2, 3)
                    new_height = driver.execute_script("return document.body.scrollHeight")
                    if new_height == last_height:
                        break
                    last_height = new_height
                
                # 获取商品链接
                product_links = driver.find_elements(By.CSS_SELECTOR, "div.gl-item a[href*='item.jd.com']")
                if not product_links:
                    print("未找到商品链接，尝试重新加载页面...")
                    driver.refresh()
                    random_sleep(5, 8)
                    continue
                    
                print(f"找到 {len(product_links)} 个商品链接")
                
                # 处理每个商品
                for i, link in enumerate(product_links, 1):
                    try:
                        print(f"正在处理第 {i}/{len(product_links)} 个商品...")
                        
                        # 获取商品URL
                        product_url = link.get_attribute('href')
                        if not product_url:
                            print("无法获取商品链接，跳过...")
                            continue
                        
                        # 在新标签页中打开商品
                        driver.execute_script(f"window.open('{product_url}', '_blank');")
                        random_sleep(2, 3)
                        
                        # 切换到新标签页
                        driver.switch_to.window(driver.window_handles[-1])
                        
                        # 获取并保存评论
                        comments = get_product_comments(driver, product_url)
                        if comments:
                            writer.writerows(comments)
                            print(f"成功保存商品评论数据")
                        
                        # 关闭当前标签页
                        driver.close()
                        
                        # 返回主标签页
                        driver.switch_to.window(driver.window_handles[0])
                        random_sleep(2, 3)
                        
                    except Exception as e:
                        print(f"处理商品时出错: {str(e)}")
                        continue
                
                # 翻到下一页
                try:
                    next_button = WebDriverWait(driver, 10).until(
                        EC.element_to_be_clickable((By.CSS_SELECTOR, "a.pn-next"))
                    )
                    safe_click(driver, next_button)
                    current_page += 1
                    random_sleep(3, 5)
                except:
                    print("无法找到下一页按钮，爬取结束")
                    break
                    
            except Exception as e:
                print(f"爬取页面时出错: {str(e)}")
                break

# 配置Edge浏览器选项
options = webdriver.EdgeOptions()
options.add_argument('--disable-gpu')  # 禁用GPU加速
options.add_argument('--no-sandbox')  # 禁用沙箱
options.add_argument('--disable-dev-shm-usage')  # 禁用/dev/shm使用
options.add_argument('--disable-extensions')  # 禁用扩展
options.add_argument('--disable-infobars')  # 禁用信息栏
options.add_argument('--disable-notifications')  # 禁用通知
options.add_argument('--disable-popup-blocking')  # 禁用弹窗拦截
options.add_argument('--disable-blink-features=AutomationControlled')  # 禁用自动化控制特征
options.add_argument('--start-maximized')  # 最大化窗口
options.add_argument('--disable-blink-features')  # 禁用Blink特征
options.add_argument('--disable-web-security')  # 禁用网页安全策略
options.add_argument('--disable-features=IsolateOrigins,site-per-process')  # 禁用站点隔离
options.add_experimental_option('excludeSwitches', ['enable-automation'])  # 排除自动化开关
options.add_experimental_option('useAutomationExtension', False)  # 禁用自动化扩展

# 初始化Edge浏览器
driver = webdriver.Edge(service=Service('D:/py/msedgedriver.exe'), options=options)

# 修改webdriver标记以绕过检测
driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
    'source': '''
        Object.defineProperty(navigator, 'webdriver', {
            get: () => undefined
        })
    '''
})

try:
    # 登录京东
    if not login_jd(driver):
        raise Exception("登录失败")
        
    # 开始爬取商品评论
    scrape_products(driver)
    print("成功爬取商品评论")

except Exception as e:
    print(f"发生错误: {str(e)}")
    driver.save_screenshot("error_screenshot.png")  # 保存错误截图
finally:
    random_sleep(3, 5)
    driver.quit()  # 关闭浏览器